package k8;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import k8.buffer.ColoursBuffer;
import k8.buffer.IndicesBuffer;
import k8.buffer.Slice;
import k8.buffer.TexCoordsBuffer;
import k8.buffer.VerticesBuffer;
import k8.primitive.Triangle;

import org.lwjgl.opengl.GL11;

public final class TexturedTriangles {

    // Global count of triangles
    public static int count = 0;

    // Map of Textures to TexturedTriangles instances
    private static Map<Texture, TexturedTriangles> map = new HashMap<Texture, TexturedTriangles>();

    // List of Triangles
    private List<Triangle> triangles;

    private Texture texture; // Texture of instance

    private VerticesBuffer vertices; // Vertices Buffer

    private TexCoordsBuffer texcoords; // Texture Cordinates Buffer

    private ColoursBuffer colours; // Colours Buffer

    private IndicesBuffer indices; // Indices Buffer

    /**
     * Gets the TexturedTriangles instance used for the specified texture.
     * 
     * @param texture
     *            A Texture
     */
    public static TexturedTriangles getInstance(Texture texture) {
        // Is there already a TexturedTriangles for the specified Texture?
        if (map.containsKey(texture))
            return map.get(texture);

        // Create the new TexturedTriangles object and add it to the Map
        TexturedTriangles tt = new TexturedTriangles(texture);
        map.put(texture, tt);
        return tt;
    }

    /** Updates the display of all textured triangles */
    public static void updateAll() {
        // Clear OpenGL buffers
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        // Call udpate() on each TexturedTriangles instance
        for (TexturedTriangles tt : map.values())
            tt.update();
    }

    /**
     * Destroys allocated OpenGL resources.
     */
    public static void destroyAll() {
        // Call destroy() on each TexturedTriangles instance
        for (TexturedTriangles tt : map.values())
            tt.destroy();
    }

    /**
     * Creates an instance of TexturedTriangles.
     * 
     * @param texture
     *            Associated texture
     */
    public TexturedTriangles(Texture texture) {
        triangles = new ArrayList<Triangle>();
        final int initialBufferCapacity = 100000;
        this.texture = texture;
        vertices = VerticesBuffer.getInstance(initialBufferCapacity);
        texcoords = TexCoordsBuffer.getInstance(initialBufferCapacity);
        colours = ColoursBuffer.getInstance(initialBufferCapacity);
        indices = IndicesBuffer.getInstance(initialBufferCapacity);
    }

    /** Destroys the associated Buffers */
    public void destroy() {
        vertices.destroy();
        texcoords.destroy();
        colours.destroy();
        indices.destroy();
    }

    /** Renders the textured triangles */
    public void update() {
        texture.glBindTexture();
        texcoords.glTexCoordPointer();
        colours.glColourPointer();
        vertices.glVertexPointer();
        indices.glDrawElements();
    }

    /**
     * Allocates Slices for the specified Triangle.
     * 
     * @param triangle
     *            Triangle to give Slices to
     */
    public void assign(Triangle triangle) {
        Slice slice;
        int elements = vertices.getElements();

        // Add the triangle to the list
        triangles.add(triangle);

        // Increment global triangle count
        count++;

        // Get a Slice for the Triangles vertices, checking for growth to
        // re-assign
        if ((slice = vertices.getSlice()) == null) {
            for (Triangle t : triangles)
                t.setVertices(vertices.getSlice());
        } else {
            triangle.setVertices(slice);
        }

        // Get a Slice for the Triangles texture coordinates, checking for
        // growth to re-assign
        if ((slice = texcoords.getSlice()) == null) {
            for (Triangle t : triangles)
                t.setTexture(texcoords.getSlice());
        } else {
            triangle.setTexture(slice);
        }

        // Get a Slice for the Triangles colours, checking for growth to
        // re-assign
        if ((slice = colours.getSlice()) == null) {
            for (Triangle t : triangles)
                t.setColours(colours.getSlice());
        } else {
            triangle.setColours(slice);
        }

        // Increment the indices buffer is needed
        if (vertices.getElements() != elements)
            indices.increment();
    }

    /** Discards the specified Triangle */
    public void discard(Triangle triangle) {
        // Remove triangle from list of triangles and decrement count
        triangles.remove(triangle);
        count--;
    }

    /** Gets the associated Texture */
    public Texture getTexture() {
        return texture;
    }
}
